﻿cargBags_Nivaya = CreateFrame('Frame', 'cargBags_Nivaya', UIParent)
cargBags_Nivaya:SetScript('OnEvent', function(self, event, ...) self[event](self, event, ...) end)
cargBags_Nivaya:RegisterEvent("ADDON_LOADED")
cargBags_Nivaya:RegisterEvent("TRADE_SHOW")
if (cargBags.Handler["Anywhere"]) then cargBags:SetActiveHandler("Anywhere") end

local CatDropDown = CreateFrame("Frame", "CatDropDown", UIParent, "UIDropDownMenuTemplate")

---------------------------------------------
---------------------------------------------
local L = cBnivL
cB_KnownItems = {}
cBniv_CatInfo = {}
cB_Bags = {}
cB_BagHidden = {}

-- Those are default values only, change them ingame via "/cbniv":
local optDefaults = {
                    NewItems = true,
                    TradeGoods = true,
                    Armor = true,
                    CoolStuff = false,
                    Junk = true,
                    AmmoAlwaysHidden = false,
                    BankBlack = false,
                    scale = 0.8,
                    FilterBank = true,
                    CompressEmpty = true,
                    Unlocked = true,
                    SortBags = true,
                    SortBank = true,
                    }

-- Those are internal settings, don't touch them at all:
local defaults =    { 
                    showAmmo = false, 
                    }

local ItemSetCaption = (IsAddOnLoaded('ItemRack') and "ItemRack ") or (IsAddOnLoaded('Outfitter') and "Outfitter ") or "Item "
local bankOpenState = false

function cargBags_Nivaya:LoadDefaults()
	cBniv = cBniv or {}
	for k,v in pairs(defaults) do
		if(type(cBniv[k]) == 'nil') then cBniv[k] = v end
	end
    cBnivCfg = cBnivCfg or {}
	for k,v in pairs(optDefaults) do
		if(type(cBnivCfg[k]) == 'nil') then cBnivCfg[k] = v end
	end
end

function cargBags_Nivaya:ADDON_LOADED(event, addon)
	if (addon ~= 'cargBags_Nivaya') then return end
	self:UnregisterEvent(event)
	self:LoadDefaults()
    if cBnivCfg.optAmmoAlwaysHidden then cBniv.showAmmo = false end
    UIDropDownMenu_Initialize(CatDropDown, cargBags_Nivaya.CatDropDownInit, "MENU")

    -----------------
    -- Frame Spawns
    -----------------

    -- Bank bags
    local t = cargBags:Spawn("cBniv_BankArmor")
    t:SetFilter(cB_Filters.fBank, true)
    t:SetFilter(cB_Filters.fBankFilter, true)
    t:SetFilter(cB_Filters.fArmorWep, true)
    cB_Bags.bankArmor = t
    
    local t = cargBags:Spawn("cBniv_BankCons")
    t:SetFilter(cB_Filters.fBank, true)
    t:SetFilter(cB_Filters.fBankFilter, true)
    t:SetFilter(cB_Filters.fConsumables, true)
    cB_Bags.bankConsumables = t

    local t = cargBags:Spawn("cBniv_BankQuest")
    t:SetFilter(cB_Filters.fBank, true)
    t:SetFilter(cB_Filters.fBankFilter, true)
    t:SetFilter(cB_Filters.fQuest, true)
    cB_Bags.bankQuest = t

    local t = cargBags:Spawn("cBniv_BankTrade")
    t:SetFilter(cB_Filters.fBank, true)
    t:SetFilter(cB_Filters.fBankFilter, true)
    t:SetFilter(cB_Filters.fTrade, true)
    cB_Bags.bankTrade = t
    
    local t = cargBags:Spawn("cBniv_Bank")
    t:SetFilter(cB_Filters.fBank, true)
    if cBnivCfg.CompressEmpty then t:SetFilter(cB_Filters.fHideEmpty, true) end
    cB_Bags.bank = t
    
    -- Keyring
    local t = cargBags:Spawn("cBniv_Keyring")
    t:SetFilter(cB_Filters.fKeyring, true)
    cB_Bags.key = t

    -- Soul Shards frame
    local t = cargBags:Spawn("cBniv_Soulshards")
    t:SetFilter(cB_Filters.fSoulShards, true)
    cB_Bags.bagSoul = t

    -- Ammo Frame
    local t = cargBags:Spawn("cBniv_Ammo")
    t:SetFilter(cB_Filters.fAmmo, true)
    cB_Bags.bagAmmo = t

    -- Frame for Item Sets
    local t = cargBags:Spawn("cBniv_ItemSets")
    t:SetFilter(cB_Filters.fItemSets, true)
    cB_Bags.bagItemSets = t

    -- Junk Filter
    local t = cargBags:Spawn("cBniv_Junk")
    t:SetFilter(cB_Filters.fJunk, true)
    cB_Bags.bagJunk = t
   
    -- New Items frame
    local t = cargBags:Spawn("cBniv_NewItems")
    t:SetFilter(cB_Filters.fNewItems, true)
    cB_Bags.bagNew = t

    -- Cool Stuff(tm)
    t = cargBags:Spawn("cBniv_Stuff")
    t:SetFilter(cB_Filters.fStuff, true)
    cB_Bags.bagStuff = t

    -- Armor and weapon frame
    local t = cargBags:Spawn("cBniv_Armor")
    t:SetFilter(cB_Filters.fArmorWep, true)
    cB_Bags.armor = t

    -- Quest frame
    local t = cargBags:Spawn("cBniv_Quest")
    t:SetFilter(cB_Filters.fQuest, true)
    cB_Bags.quest = t

    -- Consumable
    local t = cargBags:Spawn("cBniv_Consumables")
    t:SetFilter(cB_Filters.fConsumables, true)
    cB_Bags.consumables = t

    -- Trade Goods
    local t = cargBags:Spawn("cBniv_TradeGoods")
    t:SetFilter(cB_Filters.fTrade, true)
    cB_Bags.tradegoods = t
    
    -- Bagpack and bags
    local t = cargBags:Spawn("cBniv_Bag")
    t:SetFilter(cB_Filters.fBags, true)
    if cBnivCfg.CompressEmpty then t:SetFilter(cB_Filters.fHideEmpty, true) end
    cB_Bags.main = t

    -----------------------------------------------
    -- Store the anchoring order:
    -- read: "tar" is anchored to "src" in the direction denoted by "dir".
    -----------------------------------------------
    local function CreateAnchorInfo(src,tar,dir)
        tar.AnchorTo = src
        tar.AnchorDir = dir
        if src then
            if not src.AnchorTargets then src.AnchorTargets = {} end
            src.AnchorTargets[tar] = true
        end
    end

    -- Main Anchors:
    CreateAnchorInfo(nil, cB_Bags.main, "Bottom")
    CreateAnchorInfo(nil, cB_Bags.bank, "Bottom")

    cB_Bags.main:SetPoint("BOTTOMRIGHT", -20, 150)
    cB_Bags.bank:SetPoint("LEFT", 15, 0)    
    
    -- Bank Anchors:
    CreateAnchorInfo(cB_Bags.bank, cB_Bags.bankArmor, "Right")
    CreateAnchorInfo(cB_Bags.bankArmor, cB_Bags.bankTrade, "Bottom")
    
    CreateAnchorInfo(cB_Bags.bank, cB_Bags.bankConsumables, "Bottom")
    CreateAnchorInfo(cB_Bags.bankConsumables, cB_Bags.bankQuest, "Bottom")
    
    -- Bag Anchors:
    CreateAnchorInfo(cB_Bags.main, cB_Bags.key, "Bottom")

    CreateAnchorInfo(cB_Bags.main, cB_Bags.bagItemSets, "Left")
    CreateAnchorInfo(cB_Bags.bagItemSets, cB_Bags.armor, "Top")
    CreateAnchorInfo(cB_Bags.armor, cB_Bags.bagJunk, "Top")
    CreateAnchorInfo(cB_Bags.bagJunk, cB_Bags.bagNew, "Top")
    CreateAnchorInfo(cB_Bags.bagNew, cB_Bags.bagSoul, "Top")
    CreateAnchorInfo(cB_Bags.bagSoul, cB_Bags.bagAmmo, "Top")

    CreateAnchorInfo(cB_Bags.main, cB_Bags.tradegoods, "Top")
    CreateAnchorInfo(cB_Bags.tradegoods, cB_Bags.consumables, "Top")
    CreateAnchorInfo(cB_Bags.consumables, cB_Bags.quest, "Top")
    CreateAnchorInfo(cB_Bags.quest, cB_Bags.bagStuff, "Top")

    -- To toggle containers when entering / leaving a bank
    local bankToggle = CreateFrame"Frame"
    bankToggle:RegisterEvent"BANKFRAME_OPENED"
    bankToggle:RegisterEvent"BANKFRAME_CLOSED"
    bankToggle:SetScript("OnEvent", function(self, event)
        if(event == "BANKFRAME_OPENED") then
            OpenCargBank()
            OpenCargBags()
            bankOpenState = true
        else
            CloseCargBank()
            bankOpenState = false
        end
    end)

    -- Close real bank frame when our bank frame is hidden
    cB_Bags.bank:SetScript("OnHide", CloseBankFrame)

    -- Hide the original bank frame
    BankFrame:UnregisterAllEvents()

    -- Blizzard Replacement Functions
    ToggleBackpack = ToggleCargBags
    ToggleBag = function() ToggleCargBags() end
    OpenAllBags = ToggleCargBags
    CloseAllBags = CloseCargBags
    OpenBackpack = OpenCargBags
    CloseBackpack = CloseCargBags    
end

function cargBags_Nivaya:TRADE_SHOW(event) OpenCargBags() end
local function ShowBag(bag) if not cB_BagHidden[bag.Name] then bag:Show() end end

local resetNewItems = function(self)
	cB_KnownItems = {}
	for i = 0,4 do
		local tNumSlots = self:GetHandler().GetContainerNumSlots(i)
		if tNumSlots > 0 then for j = 1,tNumSlots do
			local tLink = GetContainerItemLink(i,j)
			if tLink then
				local tName = GetItemInfo(tLink)
				local _,tStackCount = GetContainerItemInfo(i,j)
				if cB_KnownItems[tName] then
					cB_KnownItems[tName] = cB_KnownItems[tName] + tStackCount
				else
					cB_KnownItems[tName] = tStackCount
				end
			end
		end end
	end
    cargBags:UpdateBags()
end

-- This function is only used inside the layout, so the cargBags-core doesn't care about it
-- It creates the border for glowing process in UpdateButton()
local createGlow = function(button, rarity)
	local glow = button:CreateTexture(nil, "OVERLAY")
	glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
	glow:SetBlendMode("ADD")
    glow:SetAlpha((rarity > 1) and .6 or .4)
	glow:SetWidth(65)
	glow:SetHeight(65)
	glow:SetPoint("CENTER", button)
	button.Glow = glow
end

-- The main function for updating an item button,
-- the item-table holds all data known about the item the button is holding, e.g.
--   bagID, slotID, texture, count, locked, quality - from GetContainerItemInfo()
--   link - well, from GetContainerItemLink() ofcourse ;)
--   name, link, rarity, level, minLevel, type, subType, stackCount, equipLoc - from GetItemInfo()
-- if you need cooldown item data, use self:RequestCooldownData()
local UpdateButton = function(self, button, item)
	button.Icon:SetTexture(item.texture)
    if IsAddOnLoaded("Tabard-O-Matic") then
		local slot = button:GetID()
		local link = item.link
		if (link) then
			local itemID = tonumber(link:match("item:(%d+)"))
			local TabardValue = TabardTextures[itemID]
			if TabardValue then Tabard_O_Matic:SetTheButtons(button, TabardValue.ItemName) end
		end
	end	    
	SetItemButtonCount(button, item.count)
	SetItemButtonDesaturated(button, item.locked, 0.5, 0.5, 0.5)

	-- Color the button's border based on the item's rarity / quality!
	if(item.rarity and item.rarity >= 1) then
		if(not button.Glow) then createGlow(button, item.rarity) end
		button.Glow:SetVertexColor(GetItemQualityColor(item.rarity))
		button.Glow:Show()
	else
		if(button.Glow) then button.Glow:Hide() end
	end
end

-- Updates if the item is locked (currently moved by user)
--   bagID, slotID, texture, count, locked, quality - from GetContainerItemInfo()
-- if you need all item data, use self:RequestItemData()
local UpdateButtonLock = function(self, button, item)
	SetItemButtonDesaturated(button, item.locked, 0.5, 0.5, 0.5)
end

-- Updates the item's cooldown
--   cdStart, cdFinish, cdEnable - from GetContainerItemCooldown()
-- if you need all item data, use self:RequestItemData()
local UpdateButtonCooldown = function(self, button, item)
	if(button.Cooldown) then
		CooldownFrame_SetTimer(button.Cooldown, item.cdStart, item.cdFinish, item.cdEnable) 
	end
end

local GetNumFreeSlots = function(bagType)
    local free, max = 0, 0
    if bagType == "bag" then
        for i = 0,4 do
            free = free + cargBags:GetHandler().GetContainerNumFreeSlots(i)
            max = max + cargBags:GetHandler().GetContainerNumSlots(i)
        end
    else
        local containerIDs = {-1,5,6,7,8,9,10,11}
        for _,i in next, containerIDs do    
            free = free + cargBags:GetHandler().GetContainerNumFreeSlots(i)
            max = max + cargBags:GetHandler().GetContainerNumSlots(i)
        end
    end
    return free, max
end

local GetFirstFreeSlot = function(bagtype)
    if bagtype == "bag" then
        for i = 0,4 do
            local t = cargBags:GetHandler().GetContainerNumFreeSlots(i)
            if t > 0 then
                local tNumSlots = cargBags:GetHandler().GetContainerNumSlots(i)
                for j = 1,tNumSlots do
                    local tLink = GetContainerItemLink(i,j)
                    if not tLink then return i,j end
                end
            end
        end
    else
        local containerIDs = {-1,5,6,7,8,9,10,11}
        for _,i in next, containerIDs do
            local t = cargBags:GetHandler().GetContainerNumFreeSlots(i)
            if t > 0 then
                local tNumSlots = cargBags:GetHandler().GetContainerNumSlots(i)
                for j = 1,tNumSlots do
                    local tLink = GetContainerItemLink(i,j)
                    if not tLink then return i,j end
                end
            end
        end    
    end
    return false
end

local function QuickSort(tTable)
    local function QS_int(left,right,t,c)
        if left < right then
            local l, r = left, right
            local pe = t[floor((l+r)/2)][c] -- pivot element
            if pe then 
                while l < r do
                    if t[l][c] then while t[l][c] > pe do l = l + 1 end end
                    if t[r][c] then while t[r][c] < pe do r = r - 1 end end
                    if l <= r then
                        local tItem = t[l]
                        t[l] = t[r]
                        t[r] = tItem
                        l = l + 1
                        r = r - 1
                    end
                end 
            end
            if left < r then QS_int(left, r, t, c) end
            if right > l then QS_int(l, right, t, c) end
        end
    end

    local function tcount(tTable)
        local n = #tTable
        if (n == 0) then for _ in pairs(tTable) do n = n + 1 end end
        return n
    end

    -- sort by quality first:
    local tNum = tcount(tTable)
    QS_int(1, tNum, tTable, 2)
    
    -- then sort by itemID:
    local s, tQ = 1, 0
    if tTable[1] then tQ = tTable[1][2] end
    for e,v in ipairs(tTable) do
        if (v[2] ~= tQ) or (e >= tNum) then
            local b = (e >= tNum) and (v[2] == tQ)
            QS_int(s, b and e or e-1, tTable, 1)
            s, tQ = e, v[2]
        end
    end

    return tTable
end

-- The function for positioning the item buttons in the bag object
local UpdateButtonPositions = function(self)
	local col, row = 0, 0
    local yPosOffs = self.Caption and 20 or 0
    local isEmpty = true

    local tName = self.Name    
    local tBankBags = string.find(tName, "cBniv_Bank%a+")
    local tBank = tBankBags or (tName == "cBniv_Bank")

    local buttonIDs = {}
  	for i, button in self:IterateButtons() do
        local tLink = GetContainerItemLink(button.bagID, button.slotID)
        if tLink then
            local _,_,tStr = string.find(tLink, "^|c%x+|H(.+)|h%[.*%]")
            local _,tID = strsplit(":", tStr)
            local _,_,tQ = GetItemInfo(tLink)
            buttonIDs[i] = { tonumber(tID), tQ, button }
        else
            buttonIDs[i] = { 0, -2, button }
        end
    end
    if (tBank and cBnivCfg.SortBank) or (not tBank and cBnivCfg.SortBags) then buttonIDs = QuickSort(buttonIDs) end

	for _,v in ipairs(buttonIDs) do
        local button = v[3]
		button:ClearAllPoints()
      
        local xPos = col * 38
        local yPos = (-1 * row * 38) - yPosOffs

        button:SetPoint("TOPLEFT", self, "TOPLEFT", xPos, yPos)
        if(col >= self.Columns-1) then
            col = 0
            row = row + 1
        else
            col = col + 1
        end
        isEmpty = false
    end

    if cBnivCfg.CompressEmpty then
        local xPos = col * 38
        local yPos = (-1 * row * 38) - yPosOffs

        local tDrop = self.DropTarget
        if tDrop then
            tDrop:ClearAllPoints()
            tDrop:SetPoint("TOPLEFT", self, "TOPLEFT", xPos, yPos)
            if(col >= self.Columns-1) then
                col = 0
                row = row + 1
            else
                col = col + 1
            end
        end
        
        cB_Bags.main.EmptySlotCounter:SetText(GetNumFreeSlots("bag"))
        cB_Bags.bank.EmptySlotCounter:SetText(GetNumFreeSlots("bank"))
    end
    
	-- This variable stores the size of the item button container
	self.ContainerHeight = (row + (col>0 and 1 or 0)) * 38

	if (self.UpdateDimensions) then self:UpdateDimensions() end -- Update the bag's height
    local t = (tName == "cBniv_Bag") or (tName == "cBniv_Bank") or (tName == "cBniv_Keyring") 
    local tAS = (tName == "cBniv_Ammo") or (tName == "cBniv_Soulshards")
    if (not tBankBags and cB_Bags.main:IsShown() and not (t or tAS)) or (tBankBags and cB_Bags.bank:IsShown()) then 
        if isEmpty then self:Hide() else self:Show() end 
    end

    cB_BagHidden[tName] = (not t) and isEmpty or false
    cargBags_Nivaya:UpdateAnchors(self)
    collectgarbage("collect")
end

-- Function is called after a button was added to an object
-- We color the borders of the button to see if it is an ammo bag or else
-- Please note that the buttons are in most cases recycled and not new created
local PostAddButton = function(self, button, bag)
	if(not button.NormalTexture) then return end

	local bagType = cargBags.Bags[button.bagID].bagType
	if(button.bagID == KEYRING_CONTAINER) then
		button.NormalTexture:SetVertexColor(1, 0.7, 0)	    -- Key ring
	elseif(bagType and bagType > 0 and bagType < 8) then
		button.NormalTexture:SetVertexColor(1, 1, 0)		-- Ammo bag
	elseif(bagType and bagType > 4) then
		button.NormalTexture:SetVertexColor(0, 1, 0)		-- Profession bags
	else
		button.NormalTexture:SetVertexColor(1, 1, 1)		-- Normal bags
	end
    
    button:SetScript('OnMouseUp', function(self, mouseButton)
        if (mouseButton == 'RightButton') and (IsAltKeyDown()) then
            local tLink = GetContainerItemLink(button.bagID, button.slotID)
            if tLink then 
                local tName = GetItemInfo(tLink) 
                CatDropDown.item = tName
                ToggleDropDownMenu(1, nil, CatDropDown, button, 0, 0)
            end
        end
    end)
end

local function PostSetPlayer() OpenCargBank() end

-- More slot buttons -> more space!
local UpdateDimensions = function(self)
	local height = 0			-- Normal margin space
	if self.BagBar and self.BagBar:IsShown() then
		height = height + 43	-- Bag button space
	end
	if self.Space then
		height = height + 16	-- additional info display space
	end
	if self.Money or self.bagToggle then
		height = height + 24
	end
	if self.Caption then	    -- Space for captions
		height = height + 20
	end
	self:SetHeight(self.ContainerHeight + height)
end

local function createTextButton(name, parent, width, height)
	local button = CreateFrame("Button", nil, parent)
	button:SetNormalFontObject(GameFontHighlightSmall)
	button:SetText(name)
	button:SetWidth(width)
	button:SetHeight(height)
	button:SetScript("OnEnter", buttonEnter)
	button:SetScript("OnLeave", buttonLeave)
	button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
	button:SetBackdrop({
		bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
		edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
		tile = true, tileSize = 16, edgeSize = 16,
		insets = {left = 4, right = 4, top = 4, bottom = 4},
	})
	button:SetBackdropColor(.3, .3, .3, .9)
	button:SetBackdropBorderColor(.5, .5, .5, .7)
	return button
end

local SetFrameMovable = function(f, v)
    f:SetMovable(true)
    f:SetUserPlaced(true)
    f:RegisterForClicks("LeftButton", "RightButton")
    if v then 
        f:SetScript("OnMouseDown", function() 
            f:ClearAllPoints() 
            f:StartMoving() 
        end)
        f:SetScript("OnMouseUp",  f.StopMovingOrSizing)
    else
        f:SetScript("OnMouseDown", nil)
        f:SetScript("OnMouseUp", nil)
    end
end

-- Style of the bag and its contents
local func = function(settings, self, type)
    local tName = self.Name
    local tBag, tBank, tKey = (tName == "cBniv_Bag"), (tName == "cBniv_Bank"), (tName == "cBniv_Keyring")
    local tBankBags = string.find(tName, "cBniv_Bank")
	self:EnableMouse(true)
    
	self.UpdateDimensions = UpdateDimensions
	self.UpdateButtonPositions = UpdateButtonPositions
	self.UpdateButton = UpdateButton
	self.UpdateButtonLock = UpdateButtonLock
	self.UpdateButtonCooldown = UpdateButtonCooldown
	self.PostAddButton = PostAddButton
    cargBags:GetHandler().PostSetPlayer = PostSetPlayer
    
	self:SetFrameStrata("HIGH")
	tinsert(UISpecialFrames, self:GetName()) -- Close on "Esc"

	if tBag or tBank then 
        SetFrameMovable(self, cBnivCfg.Unlocked) 
    end

    self.Columns = (tBank and not cBnivCfg.FilterBank) and 14 or 8
	self.ContainerHeight = 0
	self:UpdateDimensions()
	self:SetWidth(38*self.Columns)

	-- The frame background
    local tBankCustom = (tBankBags and not cBnivCfg.BankBlack)
	local color_rb = 0
	local color_gb = tBankCustom and .4 or 0
	local color_bb = tBankCustom and .6 or 0
	local alpha_fb = .9

	local background = CreateFrame("Frame", nil, self)
	background:SetBackdrop{
		bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
		edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
		tile = true, tileSize = 16, edgeSize = 16,
		insets = {left = 4, right = 4, top = 4, bottom = 4},
	}
	background:SetFrameStrata("HIGH")
    background:SetFrameLevel(1)
	background:SetBackdropColor(color_rb,color_gb,color_bb,alpha_fb)
	background:SetBackdropBorderColor(color_rb,color_gb,color_bb,1)

	background:SetPoint("TOPLEFT", -6, 6)
	background:SetPoint("BOTTOMRIGHT", 6, -6)

	-- Caption and close button
	local caption = background:CreateFontString(background, "OVERLAY", "GameFontNormal")
	if(caption) then
		local t = L.bagCaptions[self.Name]
		if not t then t = self.Name end
        if self.Name == "cBniv_ItemSets" then t=ItemSetCaption..t end
		caption:SetText(t)
		caption:SetPoint("TOPLEFT", 8, -6)
		self.Caption = caption
        
        if tBag or tBank then
            local close = CreateFrame("Button", nil, self, "UIPanelCloseButton")
            close:SetPoint("TOPRIGHT", 5, 8)
            if tBank then
                close:SetScript("OnClick", function(self) self:GetParent():Hide() end)
            else
                close:SetScript("OnClick", function(self) CloseCargBags() end)
            end
        end
	end

    local tBtnOffs = 0
  	if tBag or tBank then
		-- The money display
        if tBag then
            local money = self:SpawnPlugin("Money")
            if money then money:SetPoint("BOTTOMLEFT", self, 0, 4) end
            self.anywhere = self:SpawnPlugin("Anywhere")
            if self.anywhere then 
                self.anywhere:SetPoint("TOPRIGHT", self, -20, 4)
                tBtnOffs = 20
            end
        end

		 -- Bag bar for changing bags
		local bagType = tBag and "bags" or "bank"
		local bagButtons = self:SpawnPlugin("BagBar", bagType)
		if(bagButtons) then
			bagButtons:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 0, 23)
			bagButtons:Hide()

			-- main window gets a fake bag button for toggling key ring
			if tBag then
				local keytoggle = bagButtons:CreateKeyRingButton()
				keytoggle:SetScript("OnClick", function()
					if cBniv_Keyring:IsShown() then
						cBniv_Keyring:Hide()
						keytoggle:SetChecked(0)
					else
						cBniv_Keyring:Show()
						keytoggle:SetChecked(1)
					end
				end)
			end
		end
        self.bagButtons = bagButtons
		
		-- We don't need the bag bar every time, so let's create a toggle button for them to show
        self.bagToggle = createTextButton("Bags", self, 40, 20)
        self.bagToggle:SetPoint("BOTTOMRIGHT", self,"BOTTOMRIGHT",0,0)
        self.bagToggle:SetScript("OnClick", function()
			if(self.BagBar:IsShown()) then self.BagBar:Hide() else self.BagBar:Show() end
			self:UpdateDimensions()
		end)

		-- Button to reset new items:
        if tBag and cBnivCfg.NewItems then
            local resetBtn = createTextButton("Reset New", self, 70, 20)
            resetBtn:SetPoint("TOPRIGHT", self, "TOPRIGHT", -22-tBtnOffs, 2)
            resetBtn:SetScript("OnClick", function() resetNewItems(self) end)
        end
	
        if tBag then
            -- Button to toggle Ammo/Shard bag:
            local AmmoBtn = createTextButton("A/S", self, 35, 20)
            AmmoBtn:SetPoint("BOTTOMRIGHT", self.bagToggle, "BOTTOMLEFT", 0, 0)
            AmmoBtn:SetScript("OnClick", function() 
                if not cBniv.showAmmo then
                    cBniv.showAmmo = true
                    ShowBag(cB_Bags.bagSoul)
                    ShowBag(cB_Bags.bagAmmo)
                else 
                    cBniv.showAmmo = false
                    cB_Bags.bagSoul:Hide()
                    cB_Bags.bagAmmo:Hide()                
                end
            end)
            
            -- Button to open bank when Anywhere is active:
            if self.anywhere then
                local BankBtn = createTextButton("Bank", self, 45, 20)
                BankBtn:SetPoint("RIGHT", AmmoBtn, "LEFT")
                BankBtn:SetScript("OnClick", function() ToggleCargBank() end)
            end
        end        
    end

	-- For purchasing bank slots
	if tBank then
		local purchase = self:SpawnPlugin("Purchase")
		if(purchase) then
			purchase:SetText(BANKSLOTPURCHASE)
			purchase:SetPoint("TOPLEFT", self.bagButtons, "BOTTOMLEFT", 0, -2)
			if(self.BagBar) then purchase:SetParent(self.BagBar) end

			purchase.Cost = self:SpawnPlugin("Money", "static")
			purchase.Cost:SetParent(purchase)
			purchase.Cost:SetPoint("LEFT", purchase, "RIGHT", 4, 0)
		end
	end

    -- Item drop target
    if cBnivCfg.CompressEmpty and (tBag or tBank) then
        self.DropTarget = CreateFrame("Button", nil, self, "ItemButtonTemplate")
        self.DropTarget:SetWidth(37)
        self.DropTarget:SetHeight(37)
        
        local DropTargetProcessItem = function()
            if CursorHasItem() then
                local bID, sID = GetFirstFreeSlot(tBag and "bag" or "bank")
                if bID then PickupContainerItem(bID, sID) end
            end
        end
        self.DropTarget:SetScript("OnMouseUp", DropTargetProcessItem)
        self.DropTarget:SetScript("OnReceiveDrag", DropTargetProcessItem)
        
        local fs = self:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
        fs:SetShadowColor(0,0,0)
        fs:SetShadowOffset(0.8, -0.8)
        fs:SetTextColor(1,1,1)
        fs:SetJustifyH("LEFT")
        fs:SetPoint("BOTTOMRIGHT", self.DropTarget, "BOTTOMRIGHT", -5, 3)
        self.EmptySlotCounter = fs
    end
    
	self:SetScale(cBnivCfg.scale)
	return self
end

cargBags:RegisterStyle("Nivaya", setmetatable({}, {__call = func}))

-- Opening / Closing Functions
function cargBags_Nivaya:UpdateAnchors(self)
    if not self.AnchorTargets then return end
    for v,_ in pairs(self.AnchorTargets) do
        local t, u = v.AnchorTo, v.AnchorDir
        if t then
            local h = cB_BagHidden[t.Name]
            v:ClearAllPoints()
            if      not h   and u == "Top"      then v:SetPoint("BOTTOM", t, "TOP", 0, 15)
            elseif  h       and u == "Top"      then v:SetPoint("BOTTOM", t, "BOTTOM")
            elseif  not h   and u == "Bottom"   then v:SetPoint("TOP", t, "BOTTOM", 0, -15)
            elseif  h       and u == "Bottom"   then v:SetPoint("TOP", t, "TOP")
            elseif u == "Left" then v:SetPoint("BOTTOMRIGHT", t, "BOTTOMLEFT", -15, 0)
            elseif u == "Right" then v:SetPoint("TOPLEFT", t, "TOPRIGHT", 15, 0) end
        end
    end
end

function OpenCargBags()
    cB_Bags.main:Show()
    ShowBag(cB_Bags.armor)
	ShowBag(cB_Bags.bagNew)
    ShowBag(cB_Bags.bagItemSets)
	ShowBag(cB_Bags.quest)
    ShowBag(cB_Bags.consumables)
    ShowBag(cB_Bags.tradegoods)
	ShowBag(cB_Bags.bagStuff)
    ShowBag(cB_Bags.bagJunk)
    if cBniv.showAmmo and not cBnivCfg.AmmoAlwaysHidden then
        ShowBag(cB_Bags.bagSoul)
        ShowBag(cB_Bags.bagAmmo)
    end
end

function CloseCargBags()
	cB_Bags.main:Hide()
	cB_Bags.armor:Hide()
	cB_Bags.bagNew:Hide()
	cB_Bags.bagItemSets:Hide()
	cB_Bags.quest:Hide()
	cB_Bags.consumables:Hide()
    cB_Bags.tradegoods:Hide()
	cB_Bags.bagStuff:Hide()
    cB_Bags.bagJunk:Hide()
	cB_Bags.key:Hide()
    cB_Bags.bagSoul:Hide()
    cB_Bags.bagAmmo:Hide()
    if cBnivCfg.AmmoAlwaysHidden then cBniv.showAmmo = false end
end

function ToggleCargBags(forceopen)
	if (cB_Bags.main:IsShown() and not forceopen) then CloseCargBags() else OpenCargBags() end
end

function OpenCargBank()
    cB_Bags.bank:Show()
    ShowBag(cB_Bags.bankArmor)
    ShowBag(cB_Bags.bankQuest)
    ShowBag(cB_Bags.bankTrade)
    ShowBag(cB_Bags.bankConsumables)
end

function CloseCargBank()
    cB_Bags.bank:Hide()
    cB_Bags.bankArmor:Hide()
    cB_Bags.bankQuest:Hide()
    cB_Bags.bankTrade:Hide()
    cB_Bags.bankConsumables:Hide()
end

function ToggleCargBank()
	if cB_Bags.bank:IsShown() then CloseCargBank() else OpenCargBank() end
end

local function StatusMsg(str1, str2, data, name, short)
    local R,G,t = '|cFFFF0000', '|cFF00FF00', ''
    if (data ~= nil) then t = data and G..(short and 'on|r' or 'enabled|r') or R..(short and 'off|r' or 'disabled|r') end
    t = (name and '|cFFFFFF00cargBags_Nivaya:|r ' or '')..str1..t..str2
    ChatFrame1:AddMessage(t)
end

local function StatusMsgVal(str1, str2, data, name)
    local G,t = '|cFF00FF00', ''
    if (data ~= nil) then t = G..data..'|r' end
    t = (name and '|cFFFFFF00cargBags_Nivaya:|r ' or '')..str1..t..str2
    ChatFrame1:AddMessage(t)
end

local function HandleSlash(str)
    local str,str2 = strsplit(" ", str)
    if str == 'new' then
        cBnivCfg.NewItems = not cBnivCfg.NewItems
        StatusMsg('The "New Items" filter is now ', '.', cBnivCfg.NewItems, true, false)
    elseif str == 'trade' then
        cBnivCfg.TradeGoods = not cBnivCfg.TradeGoods
        StatusMsg('The "Trade Goods" filter is now ', '.', cBnivCfg.TradeGoods, true, false)
    elseif str == 'armor' then
        cBnivCfg.Armor = not cBnivCfg.Armor
        StatusMsg('The "Armor and Weapons" filter is now ', '.', cBnivCfg.Armor, true, false)
    elseif str == 'cool' then
        cBnivCfg.CoolStuff = not cBnivCfg.CoolStuff
        StatusMsg('The "Cool Stuff" filter is now ', '.', cBnivCfg.CoolStuff, true, false)
    elseif str == 'junk' then
        cBnivCfg.Junk = not cBnivCfg.Junk
        StatusMsg('The "Junk" filter is now ', '.', cBnivCfg.Junk, true, false)
    elseif str == 'ammo' then
        cBnivCfg.AmmoAlwaysHidden = not cBnivCfg.AmmoAlwaysHidden
        StatusMsg('Hiding Ammo/Shard bags by default is now ', '.', cBnivCfg.AmmoAlwaysHidden, true, false)
    elseif str == 'bankbg' then
        cBnivCfg.BankBlack = not cBnivCfg.BankBlack
        StatusMsg('Black background color for the bank is now ', '. Reload your UI for this change to take effect!', cBnivCfg.BankBlack, true, false)
    elseif str == 'bankfilter' then
        cBnivCfg.FilterBank = not cBnivCfg.FilterBank
        StatusMsg('Bank filtering is now ', '. Reload your UI for this change to take effect!', cBnivCfg.FilterBank, true, false)
    elseif str == 'empty' then
        cBnivCfg.CompressEmpty = not cBnivCfg.CompressEmpty
        StatusMsg('Empty bagspace compression is now ', '. Reload your UI for this change to take effect!', cBnivCfg.CompressEmpty, true, false)
    elseif str == 'unlock' then
        cBnivCfg.Unlocked = not cBnivCfg.Unlocked
        SetFrameMovable(cB_Bags.main, cBnivCfg.Unlocked)
        SetFrameMovable(cB_Bags.bank, cBnivCfg.Unlocked)
        StatusMsg('Movable bags are now ', '.', cBnivCfg.Unlocked, true, false)
    elseif str == 'sortbags' then
        cBnivCfg.SortBags = not cBnivCfg.SortBags
        StatusMsg('Auto sorting bags is now ', '. Reload your UI for this change to take effect!', cBnivCfg.SortBags, true, false)
    elseif str == 'sortbank' then
        cBnivCfg.SortBank = not cBnivCfg.SortBank
        StatusMsg('Auto sorting bank is now ', '. Reload your UI for this change to take effect!', cBnivCfg.SortBank, true, false)
    elseif str == 'scale' then
        local t = tonumber(str2)
        if t then
            cBnivCfg.scale = t
            for _,v in pairs(cB_Bags) do v:SetScale(cBnivCfg.scale) end
            StatusMsgVal('Overall scale has been set to ', '.', cBnivCfg.scale, true)
        else
            StatusMsg('You have to specify a value, e.g. /cbniv scale 0.8.', '', nil, true, false)
        end
    else
        ChatFrame1:AddMessage('|cFFFFFF00cargBags_Nivaya:|r')
        StatusMsg('(', ') unlock - Toggle unlocked status.', cBnivCfg.Unlocked, false, true)
        StatusMsg('(', ') new - Toggle the "New Items" filter.', cBnivCfg.NewItems, false, true)
        StatusMsg('(', ') trade - Toggle the "Trade Goods" filter .', cBnivCfg.TradeGoods, false, true)
        StatusMsg('(', ') armor - Toggle the "Armor and Weapons" filter .', cBnivCfg.Armor, false, true)
        StatusMsg('(', ') cool - Toggle the "Cool Stuff" filter.', cBnivCfg.CoolStuff, false, true)
        StatusMsg('(', ') junk - Toggle the "Junk" filter.', cBnivCfg.Junk, false, true)        
        StatusMsg('(', ') ammo - Toggle Hiding Ammo/Shard bags by default.', cBnivCfg.AmmoAlwaysHidden, false, true)
        StatusMsg('(', ') bankbg - Toggle black bank background color.', cBnivCfg.BankBlack, false, true)
        StatusMsg('(', ') bankfilter - Toggle bank filtering.', cBnivCfg.FilterBank, false, true)
        StatusMsg('(', ') empty - Toggle empty bagspace compression.', cBnivCfg.CompressEmpty, false, true)
        StatusMsg('(', ') sortbags - Toggle auto sorting the bags.', cBnivCfg.SortBags, false, true)
        StatusMsg('(', ') sortbank - Toggle auto sorting the bank.', cBnivCfg.SortBank, false, true)
        StatusMsgVal('(', ') scale [number] - Set the overall scale.', cBnivCfg.scale, false)
    end
    cargBags:UpdateBags()
end

SLASH_CBNIV1 = '/cbniv'
SlashCmdList.CBNIV = HandleSlash

function cargBags_Nivaya:CatDropDownInit()
    level = 1
    local info = UIDropDownMenu_CreateInfo()
  
    local function AddInfoItem(caption)
        local t = L.bagCaptions[caption]
        info.text = t and t or caption
        info.value = caption
        info.func = function() cargBags_Nivaya:CatDropDownOnClick() end
        info.owner = this:GetParent()
        UIDropDownMenu_AddButton(info, level)
    end

    AddInfoItem("cBniv_Armor")
    AddInfoItem("cBniv_Consumables")
    AddInfoItem("cBniv_Quest")
    AddInfoItem("cBniv_TradeGoods")
    AddInfoItem("cBniv_Stuff")
    AddInfoItem("cBniv_ItemSets")
    AddInfoItem("cBniv_Ammo")
    AddInfoItem("cBniv_Junk")
    AddInfoItem("cBniv_Bag")
end

function cargBags_Nivaya:CatDropDownOnClick()
    local value = this.value
    local item = CatDropDown.item
    cBniv_CatInfo[item] = value
    cargBags:UpdateBags()
end